﻿//------------------------------------------------------------------------------
// Copyright © FRA & FV 2014
// All rights reserved
//------------------------------------------------------------------------------
// Binary Serialization SDK
//
// SVN revision information:
//   $Revision: 98 $
//------------------------------------------------------------------------------
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace FRAFV.Binary.Serialization.CodeDom
{
	public static class CodeExpressionExtensions
	{
		#region Code cast expressions

		public static CodeExpression Cast(this CodeExpression method, string type)
		{
			if (type.IsNullEmpty()) return method;
			Type t;
			if (DeclarationExpressionExtensions.reservedTypes.TryGetValue(type, out t))
				return method.Cast(t);
			return new CodeCastExpression(type, method);
			//var sw = new StringWriter();
			//ClassGenerator.provider.Value.GenerateCodeFromExpression(method, sw, ClassGenerator.options.Value);
			//return String.Format("(({1}){0})", sw, type).AsSnippet();
		}

		public static CodeExpression Cast<TValue>(this CodeExpression value)
		{
			return value.Cast(typeof(TValue));
		}

		public static CodeExpression Cast(this CodeExpression value, Type type)
		{
			return new CodeCastExpression(type, value);
		}

		#endregion

		#region Invoke expressions

		public static CodeMethodInvokeExpression MethodInvoke(this CodeExpression target, string method, params object[] args)
		{
			return new CodeMethodInvokeExpression(target, method,
				args.Select(arg => arg.AsExpression()).ToArray());
		}

		#endregion

		#region Property expressions

		public static CodePropertyReferenceExpression Property(this CodeExpression value, string property)
		{
			return new CodePropertyReferenceExpression(value, property);
		}

		public static CodeIndexerExpression Index(this CodeExpression value, string index)
		{
			return new CodeIndexerExpression(value, index.AsVariable());
		}

		#endregion

		#region Binary operator expressions

		public static CodeBinaryOperatorExpression Add(this CodeExpression sum, object value)
		{
			return new CodeBinaryOperatorExpression(sum, CodeBinaryOperatorType.Add, value.AsExpression());
		}

		public static CodeBinaryOperatorExpression Sub(this CodeExpression sum, object value)
		{
			return new CodeBinaryOperatorExpression(sum, CodeBinaryOperatorType.Subtract, value.AsExpression());
		}

		public static CodeBinaryOperatorExpression Mul(this CodeExpression sum, object value)
		{
			return new CodeBinaryOperatorExpression(sum, CodeBinaryOperatorType.Multiply, value.AsExpression());
		}

		public static CodeBinaryOperatorExpression And(this CodeExpression left, CodeExpression right)
		{
			return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanAnd, right);
		}

		public static CodeBinaryOperatorExpression Or(this CodeExpression left, CodeExpression right)
		{
			return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanOr, right);
		}

		public static CodeBinaryOperatorExpression IsNull(this CodeExpression value)
		{
			return new CodeBinaryOperatorExpression(value, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
		}

		public static CodeBinaryOperatorExpression IsNotNull(this CodeExpression value)
		{
			return new CodeBinaryOperatorExpression(value, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
		}

		public static CodeBinaryOperatorExpression Equality(this CodeExpression left, object right)
		{
			return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityEquality, right.AsExpression());
		}

		public static CodeBinaryOperatorExpression Inequality(this CodeExpression left, object right)
		{
			return new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.IdentityInequality, right.AsExpression());
		}

		public static CodeExpression Is(this CodeExpression left, string right)
		{
			//return new CodeBinaryOperatorExpression(left.IsNotNull(), CodeBinaryOperatorType.BooleanAnd,
			//	right.MethodInvoke("IsAssignableFrom", left.MethodInvoke("GetType")));
			var sw = new StringWriter();
			sw.Write('(');
			ClassGenerator.provider.Value.GenerateCodeFromExpression(left, sw, ClassGenerator.options.Value);
			sw.Write(" is ");
			ClassGenerator.provider.Value.GenerateCodeFromExpression(new CodeTypeReferenceExpression(right.AsType()), sw, ClassGenerator.options.Value);
			sw.Write(')');
			return sw.ToString().AsSnippet();
		}

		public static CodeStatement Increment(this CodeArgumentReferenceExpression arg, object value)
		{
			//new CodeAssignStatement(arg, new CodeBinaryOperatorExpression(arg, CodeBinaryOperatorType.Add,
			return new CodeAttachEventStatement(null, arg.ParameterName, value.AsExpression());
		}

		public static CodeStatement Increment(this CodeVariableReferenceExpression arg, object value)
		{
			//new CodeAssignStatement(arg, new CodeBinaryOperatorExpression(arg, CodeBinaryOperatorType.Add,
			return new CodeAttachEventStatement(null, arg.VariableName, value.AsExpression());
		}

		public static CodeStatement Decrement(this CodeArgumentReferenceExpression arg, object value)
		{
			//new CodeAssignStatement(arg, new CodeBinaryOperatorExpression(arg, CodeBinaryOperatorType.Subtract,
			return new CodeRemoveEventStatement(null, arg.ParameterName, value.AsExpression());
		}

		public static CodeStatement Decrement(this CodeVariableReferenceExpression arg, object value)
		{
			//new CodeAssignStatement(arg, new CodeBinaryOperatorExpression(arg, CodeBinaryOperatorType.Subtract,
			return new CodeRemoveEventStatement(null, arg.VariableName, value.AsExpression());
		}

		public static CodeExpression SumValues<TValue>(this IEnumerable<TValue> values)
		{
			var sum = values.FirstOrDefault().AsExpression();
			foreach (var v in values.Skip(1))
				sum = sum.Add(v);
			return sum;
		}

		public static CodeExpression AndValues<TValue>(this IEnumerable<TValue> values)
			where TValue : CodeExpression
		{
			CodeExpression sum = values.FirstOrDefault();
			foreach (var v in values.Skip(1))
				sum = sum.And(v);
			return sum;
		}

		public static CodeExpression OrValues<TValue>(this IEnumerable<TValue> values)
			where TValue : CodeExpression
		{
			CodeExpression sum = values.FirstOrDefault();
			foreach (var v in values.Skip(1))
				sum = sum.Or(v);
			return sum;
		}

		#endregion

		#region Condition expressions

		public static CodeExpression Condition(this CodeExpression condition, object trueValue, object falseValue)
		{
			//var cond = new CodeConditionStatement(condition);
			//cond.TrueStatements.Add(trueValue.AsExpression());
			//cond.FalseStatements.Add(falseValue.AsExpression());
			//return cond;
			var sw = new StringWriter();
			ClassGenerator.provider.Value.GenerateCodeFromExpression(condition, sw, ClassGenerator.options.Value);
			sw.Write(" ? ");
			ClassGenerator.provider.Value.GenerateCodeFromExpression(trueValue.AsExpression(), sw, ClassGenerator.options.Value);
			sw.Write(" : ");
			ClassGenerator.provider.Value.GenerateCodeFromExpression(falseValue.AsExpression(), sw, ClassGenerator.options.Value);
			return sw.ToString().AsSnippet();
		}

		public static CodeExpression Coalesce(this CodeExpression value, object nullValue)
		{
			//var cond = new CodeConditionStatement(value.IsNull());
			//cond.TrueStatements.Add(nullValue.AsExpression());
			//cond.FalseStatements.Add(value);
			//return cond;
			var sw = new StringWriter();
			ClassGenerator.provider.Value.GenerateCodeFromExpression(value, sw, ClassGenerator.options.Value);
			sw.Write(" ?? ");
			ClassGenerator.provider.Value.GenerateCodeFromExpression(nullValue.AsExpression(), sw, ClassGenerator.options.Value);
			return sw.ToString().AsSnippet();
		}

		#endregion
	}
}
